#include "game.h"

Game::Game ()
{
	generate_map();
	generate_objects();	
	count = 0;
	_is_shooting = false; 
}



void Game::generate_map()
{
//	_map.setBitmap();
	_map.generate_default_map();
}

void Game::generate_objects()
{
	_digdug = Movables(DIGDUG, 20, 10); 
	_map.plot_movable_on_map(_digdug);
	_monsters.push_back(Movables(MONSTER, 6, 8));
	_map.plot_movable_on_map(_monsters.at(0));
	_monsters.push_back(Movables(MONSTER,  3, 23));
	_map.plot_movable_on_map(_monsters.at(1));
	_monsters.push_back(Movables(MONSTER,  36, 18));
	_map.plot_movable_on_map(_monsters.at(2));
	_projectile = Movables(PROJECTILE, 0, 0);
}

Game_result Game::update(Action instruction)
{
//Check if digdug has stopped shooting. If he has, then remove the projectile from the map
	if(instruction != SHOOT && _is_shooting == true)
	{
		_is_shooting = false;
		_map.remove_movable_from_map(_projectile);	
	}


	//Handle Digdug's action first;
	//Digdug is always the first object in the vector of movables contained in game
	//Move_result result = HAS_MOVED; 

	if(instruction != SHOOT && instruction != MOVE_NONE)
	{
		//convert enum type instruction to direction
		Direction dir = static_cast<Direction>(instruction);
		_digdug.set_direction(dir);
		if(move(_digdug, dir) == GAME_OVER)
		{
			return YOU_DEAD;
		} 

	}

//Handle the creation and movement of digdug's projectile, including killing monsters 
	if(instruction == SHOOT)
	{
		if(_is_shooting == false)
		{
			_projectile.set_direction(_digdug.direction());

			cout<<"A shot has been fired in : "<<endl; //<<enum_to_string(_projectile.direction())<<endl;

			int delta_x = 0;
			int delta_y = 0;

			switch(_projectile.direction())
			{
				case UP:	delta_y = -1;	break;
				case DOWN:	delta_y = +1;	break;		
				case LEFT:	delta_x = -1;	break;		
				case RIGHT:	delta_x = +1;	break;
			}

			int projectile_x = _digdug.x() + delta_x*(_digdug.radius() + _projectile.radius() +1);
			int projectile_y = _digdug.y() + delta_y*(_digdug.radius() + _projectile.radius() +1);
	
			_projectile.set_location(projectile_x, projectile_y);
 			if(contains(get_valid_directions(projectile_x,projectile_y,_projectile), _projectile.direction()))
			{
			_map.plot_movable_on_map(_projectile);
			_is_shooting = true;
			}
		}

		Move_result result_of_move = move(_projectile, _projectile.direction());

		if(result_of_move == OBJECT_DEAD)
		{
			//find closest monster and kill him
			int delta_x = 0;
			int delta_y = 0;

			int current_total_distance = 0;
			int prev_total_distance = 100;
			int monster_number = 0;


    		for(int i = 0; i!= _monsters.size(); i++)
			{
				delta_x = _monsters.at(i).x() - _projectile.x();
				delta_y = _monsters.at(i).y() - _projectile.y();
				current_total_distance = abs(delta_x) - abs(delta_y);

				if(abs(current_total_distance) < abs(prev_total_distance))
				{
					prev_total_distance = current_total_distance;
					monster_number = i;
				}				

			}

			//remove dead monster from map and delete him
			_map.remove_movable_from_map(_monsters.at(monster_number));
			vector<Movables>::iterator start_of_vec = _monsters.begin();
			_monsters.erase(start_of_vec + monster_number);
			//remove the projectile from the map and delete it
			_map.remove_movable_from_map(_projectile);
			_is_shooting = false;

			if(_monsters.size() ==0)
			{
				return YOU_WIN;
			}
		}


	}

//Handle movement of all other movable objects
//start moving through vector containing movable objects of the game starting at the first object after digdug	
	
			bool a_ghost = false;

			for(int i = 0; i < !_monsters.size(); i++)
			{
				if(_monsters.at(i).is_ghost())
					a_ghost = true;
			}




	for(int i = 0; i < _monsters.size(); i++)
	{
		vector<Direction> dir = get_valid_directions(_monsters.at(i).x(), _monsters.at(i).y(), _monsters.at(i));
		Direction direction;
		if(_monsters.at(i).is_ghost() == false)
		{
			if(count%1 ==0)
			{
				direction = get_direction(_monsters.at(i), _digdug);
				_monsters.at(i).set_direction(direction);
				Move_result move_res = move(_monsters.at(i), direction);

				if(move_res == GAME_OVER)
				{
					return YOU_DEAD;
				}
				string resultsss =  enum_to_string(move_res);
			}
		}
		else
		{
			if(count%2 ==0)
			move_disembodied_monster(_monsters.at(i), _digdug);
		}

		if((count+i*30 +5)%50== 0)
		{
			_monsters.at(i).is_ghost(true);
			_map.remove_movable_from_map(_monsters.at(i));
			cout<<"Setting monsnster to a ghost"<<endl;
		}

	}
	

	count++;

	return IN_PROGRESS;


}

Rules Game::get_rule(Movable_type object_type, Movable_type map_location_type)
{
	if(map_location_type == HOLE)
	{
		return CAN_MOVE;
	}

	else if(map_location_type == SAND)
	{
		if(object_type == DIGDUG)
		{
			return CAN_MOVE;
		}
		else return CANT_MOVE;
	}

	else if(map_location_type == MONSTER)
	{
		switch(object_type)
		{
		case DIGDUG:
			return DEATH_TO_DIGDUG;
			break;
		case PROJECTILE:
			return DEATH_TO_MONSTER;
			break;
		case MONSTER:
			return CAN_OVERLAP;
			break;
		}
	}
	
	else if(map_location_type == OVERLAP)
	{
		switch(object_type)
		{
		case DIGDUG:
			return DEATH_TO_DIGDUG;
			break;
		case PROJECTILE:
			return DEATH_TO_MONSTER;
			break;
		case MONSTER:
			return CAN_MOVE;
			break;
		}
	}
	

//will only happen if trying to move a monster
	else if(map_location_type == PROJECTILE)
	{
		if(object_type == MONSTER)
		return DEATH_TO_MONSTER;
	}

	else if(map_location_type == DIGDUG)
	{
		if(object_type == MONSTER)
		return DEATH_TO_DIGDUG;
	}

	return CAN_MOVE;
}

Move_result Game::move(Movables& movable, Direction direction)
{

	Move_result result = HAS_MOVED;
// get the coordinates and dimensions of the movable
	int start_x = movable.x();
	int start_y = movable.y();
	int x = movable.x(); 
	int y = movable.y();
	int radius = movable.radius();
	int length = 2*radius + 1;
	int speed = movable.speed();


	int delta_x = 0;
	int delta_y = 0;

	switch(direction)
	{
		case UP:	delta_y = -1;	break;
		case DOWN:	delta_y = +1;	break;		
		case LEFT:	delta_x = -1;	break;		
		case RIGHT:	delta_x = +1;	break;
	}



	Map temp_map = _map;

	for (int element = -radius; element <= radius && result == HAS_MOVED; element++)
	{

		int y_check = y + delta_y*(1 + radius) + delta_x*element;
		int x_check = x + delta_x*(1 + radius) + delta_y*element;

		if(x_check < 0 || x_check == game_grid_elements || y_check < 0 || y_check == game_grid_elements)
			result = CANNOT_MOVE;
		else
		{
			switch(get_rule(movable.type(), _map.element(x_check, y_check)))
			{
			case CAN_MOVE:
				_map.set_type(movable.type(), x_check, y_check);
				if (_map.element(x_check - delta_x*length, y_check  - delta_y*length) == OVERLAP)
				_map.set_type(movable.type(), x_check -delta_x*length, y_check -delta_y*length);
				else
				_map.set_type(HOLE, x_check -delta_x*length, y_check + -delta_y*length);
				break;
						
			case CANT_MOVE:
				result = CANNOT_MOVE;

				break;
			case DEATH_TO_DIGDUG:
				result = GAME_OVER;
				return result;
				break;
//!!must finish writing this case to destroy projectile and specific monster
			case DEATH_TO_MONSTER:
				result = OBJECT_DEAD;

				break;
			case CAN_OVERLAP:
				_map.set_type(OVERLAP, x_check, y_check);
				_map.set_type(HOLE, x_check -delta_x*length, y_check + -delta_y*length);
				movable.set_location(start_x + delta_x, start_y + delta_y);
						
				break;
			}
		}
					
	}

	if( result == HAS_MOVED)
	{
		movable.set_location(start_x + delta_x, start_y + delta_y);			
	}
	else _map = temp_map;


/*	if(x == start_x && y == start_y)
	{
		result = CANNOT_MOVE;
	}*/	
		
	return result;
}

Map Game::get_map()
{
	return _map;
}

Movables Game::get_movable (Movable_type type, int vector_count)
{
  switch (type)
  {
	  case DIGDUG:		return _digdug;							break;
	  case MONSTER:		return _monsters.at(vector_count);		break;
	  case PROJECTILE:	return _projectile;	break;
	  default:			return _digdug;							break;
	 // case ROCK:		return _rock.at(vector_count);			break;	
  }
}

//Returns a vector contianing all of the valid moves at that moment. 
vector <Direction> Game::get_valid_directions(int x, int y, Movables &movable)
{

	vector <Direction> direction_list;
	Direction direction;

	int radius = movable.radius();
	int length = 2*radius + 1;


	for(int i = 0; i < 4; i++)
	{
		direction = static_cast<Direction>(i);
		bool is_can_move = true;

		int delta_x = 0;
		int delta_y = 0;

		switch(direction)
		{
			case UP:	delta_y = -1;	break;
			case DOWN:	delta_y = +1;	break;		
			case LEFT:	delta_x = -1;	break;		
			case RIGHT:	delta_x = +1;	break;
		}

		for (int element = -radius; element <= radius; element++)
		{

			int y_check = y + delta_y*(1 + radius) + delta_x*element;
			int x_check = x + delta_x*(1 + radius) + delta_y*element;

			if(x_check > 0 && x_check != game_grid_elements && y_check > 0 && y_check != game_grid_elements)
			{
				if(get_rule(movable.type(), _map.element(x_check, y_check))== CANT_MOVE)
				{
					is_can_move = false;
				}
			}
			else
				is_can_move = false;
		}
		if(is_can_move)
				direction_list.push_back(direction);
	}

	return direction_list;
}

bool Game::contains(vector<Direction> list, Direction value)
{
	for(int i = 0; i < list.size(); i++)
	{
		if(list[i] == value)
			return true;
	}
	return false;
}

bool Game::contains(vector<int> &list, int value)
{
	for(int i = 0; i < list.size(); i++)
	{
		if(list[i] == value)
			return true;
	}
	return false;
}


//This will check if the new co-ordinate on the given list is occupied by a monster....
//If so, then we have found a way to route to that monster.
void Game::monster_directions_update(vector<int> & x_co_ordinates, vector <int> & y_co_ordinates) 
{
	 for(int i = 0; i < _monsters.size(); i++)
	 {
		 int x = _monsters.at(i).x();
		 int y = _monsters.at(i).y();

		 int check_x = x_co_ordinates.back();
		 int check_y = y_co_ordinates.back();

		 if( check_x== x && check_y== y)
		 {
			 //Develop a 2D co-ordinates list vector
			 vector <vector <int> > co_ordinates;
			 co_ordinates.push_back(x_co_ordinates);
			 co_ordinates.push_back(y_co_ordinates);

			 //Apply the new route
			 _monsters.at(i).set_route(co_ordinates); 
		 }
	 }
}

//The directions list is a coordinate here 
bool Game::update_list_co_ordinates (vector<vector<int>> & x_co_ordinates, vector<vector <int>> & y_co_ordinates, int list_no,  Direction dir)
{
	bool has_not_moved_backwards = true;

	//Set up the iterators for list destruction if required. 
	vector< vector< int> >::iterator x_routes_iter = x_co_ordinates.begin();
	vector< vector< int> >::iterator y_routes_iter = y_co_ordinates.begin();

	int delta_x =0; 
	int delta_y =0; 

	//Determine the new co-ordinates based on the direction
	switch(dir)
	{
		case UP:	delta_y = -1;	break;
		case DOWN:	delta_y = +1;	break;		
		case LEFT:	delta_x = -1;	break;		
		case RIGHT:	delta_x = +1;	break;
	}

	int x = x_co_ordinates.at(list_no).back()+delta_x;
	int y = y_co_ordinates.at(list_no).back()+delta_y;

	//Prevent wrapping back on self by kulling the list if it tries to insert a value that is already there
	if(contains(x_co_ordinates.at(list_no), x) && contains(y_co_ordinates.at(list_no), y))
	{
		 x_co_ordinates.erase(x_routes_iter+list_no);
		 y_co_ordinates.erase(y_routes_iter+list_no);
		 has_not_moved_backwards = false;
	}

	//Otherwise add the new values to the lists
	else
	{
		x_co_ordinates.at(list_no).push_back(x);
		y_co_ordinates.at(list_no).push_back(y);
	}

	return has_not_moved_backwards;
}


void Game::move_disembodied_monster(Movables &monster, Movables& const digdug)
{
	if (monster._disembodied_end_pos.empty())
	{
		monster._disembodied_end_pos.push_back(digdug.x());
		monster._disembodied_end_pos.push_back(digdug.y());
	}
	
	int x_pos = monster.x();
	int y_pos = monster.y();

	int delta_x = monster._disembodied_end_pos.at(0) - x_pos; 
	int delta_y = monster._disembodied_end_pos.at(1) - y_pos;

	if (delta_x < 0)
	{
		monster.set_location(x_pos - 1, y_pos); //move monster left
	}
	else if (delta_x > 0) 
	{
		monster.set_location(x_pos + 1, y_pos); //move monster right
	}
	x_pos = monster.x();


	if (delta_y < 0)
	{
		monster.set_location(x_pos, y_pos - 1); //move monster up
	}
	else if (delta_y > 0) 
	{
		monster.set_location(x_pos, y_pos + 1); //move monster down
	}
	y_pos = monster.y();

	if (delta_x == 0 && delta_y == 0)
	{
		monster._disembodied_end_pos.clear();
		monster.is_ghost(false);
		_map.plot_movable_on_map(monster);
	}
}



string Game::enum_to_string(Direction t)
{
      switch(t)
	  {
         case UP:
            return "UP";
			break;
         case DOWN:
            return "DOWN";
			break;
		 case LEFT:
            return "LEFT";
			break;
		case RIGHT:
            return "RIGHT";
			break;
         default:
            return "NONE";
      }
 }

string Game::enum_to_string(Move_result t)
{
      switch(t)
	  {
         case HAS_MOVED:
            return "HAS_MOVED";
			break;
         case CANNOT_MOVE:
            return "CANNOT_MOVE";
			break;
		 case OBJECT_DEAD:
            return "OBJECT_DEAD";
			break;
		case GAME_OVER:
            return "GAME_OVER";
			break; 
      }
 }

void Game::print_monsters(vector<Movables> &monsters)
{
	for(int i = 0; i < monsters.size(); i++)
	{
		cout<<"Monster "<< i<<":["<<monsters.at(i)._monster_route.at(0).size()<<"] :"; 
		for(int j = 0; j < monsters.at(i)._monster_route.at(0).size(); j++)
		{
		//	cout<<"(" <<monsters.at(i)._monster_route.at(j).at(0)<<","<<monsters.at(i)._monster_route.at(j).at(1)<<") ";
		}
		cout<<endl;
	}

}

Direction Game::get_direction(Movables &monster, Movables &digdug)
{
	//Know where we have come from and what our prevous options were
	 Direction previous_direction = monster.direction();
	 vector <Direction> prevous_direction_list = monster._direction_list;
	 vector <Direction> new_direction_list = get_valid_directions(monster.x(), monster.y(), monster);
	
	 //If things have changed since our last move....
	 //Then try and work out a new direction to go in. 
	 if(new_direction_list != prevous_direction_list)
	 {
		 monster._direction_list = new_direction_list;

		 Direction desired_x_direction = RIGHT;
		 Direction desired_y_direction = DOWN; 
		 Direction desired_direction; 

		 int delta_x = monster.x() - digdug.x();
		 int delta_y =  monster.y() - digdug.y();

		 if(delta_x > 0)
			 desired_x_direction = LEFT;
		 if(delta_y > 0)
			 desired_y_direction = UP;
		 
		 if(new_direction_list.size() == 1)
		 {
			 return new_direction_list.at(0);
		 }

		 if(abs(delta_x) > abs(delta_y) && contains(new_direction_list, desired_x_direction))
		 {
			 desired_direction = desired_x_direction;
			 return desired_direction;
		 }
		 else if (contains(new_direction_list, desired_y_direction))
		 {
			 desired_direction = desired_y_direction; 
			 return desired_direction;
		 }
		 else if(contains(new_direction_list, previous_direction)) 
		 {
			 return previous_direction;
		 }

	 }
	 else
	 {
		 return previous_direction;
	 }
}